`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date:    17:47:50 10/29/2011 
// Design Name: 
// Module Name:    up_stream_bridge 
// Project Name: 
// Target Devices: 
// Tool versions: 
// Description: 
//
// Dependencies: 
//
// Revision: 
// Revision 0.01 - File Created
// Additional Comments: 
//
//////////////////////////////////////////////////////////////////////////////////
module axis_double_width #(
  parameter DWIDTH = 8,
  parameter KWIDTH = DWIDTH/8
)(
  input                   clk,
  input                   rst,
      
  input                   s_tvalid,
  output                  s_tready,
  input   [DWIDTH-1:0]    s_tdata,
  input   [KWIDTH-1:0]    s_tkeep,
  input                   s_tlast,

  output                  m_tvalid,
  input                   m_tready,
  output  [DWIDTH*2-1:0]  m_tdata,
  output  [2*KWIDTH-1:0]  m_tkeep,
  output                  m_tlast
  );
  reg [0:0]         beat_count = 0;
  reg [DWIDTH-1:0]  lo_tdata = 0;
  reg [KWIDTH-1:0]  lo_tkeep = 0;
  
  always @(posedge clk)
  begin
    if (rst || (s_tvalid && s_tready && s_tlast))
      beat_count <= 0;
    else if(s_tvalid && s_tready)
      beat_count <= beat_count + 1;
  end
  
  always @(posedge clk)
  begin
    if(s_tvalid && s_tready && !beat_count[0])
    begin
      lo_tdata <= s_tdata;
      lo_tkeep <= s_tkeep;
    end
  end
  
  assign m_tvalid                   = (beat_count[0] || s_tlast) && s_tvalid;
  assign s_tready                   = (!beat_count[0])? 1'b1 : m_tready;
  assign m_tdata[DWIDTH-1:0]        = (!beat_count[0] && s_tlast) ? s_tdata : lo_tdata;
  assign m_tdata[DWIDTH*2-1:DWIDTH] = s_tdata;
  assign m_tkeep[KWIDTH-1:0]        = (!beat_count[0] && s_tlast) ? s_tkeep : lo_tkeep;
  assign m_tkeep[2*KWIDTH-1:KWIDTH] = (!beat_count[0] && s_tlast) ? {(KWIDTH){1'b0}} : s_tkeep;
  assign m_tlast = s_tlast;
  
endmodule

module axis_quad_width #(
  parameter DWIDTH = 8,
  parameter KWIDTH = DWIDTH/8
)(
  input                       clk,
  input                       rst,
      
  input                       s_tvalid,
  output  reg                 s_tready,
  input       [DWIDTH-1:0]    s_tdata,
  input       [KWIDTH-1:0]    s_tkeep,
  input                       s_tlast,

  output  reg                 m_tvalid,
  input                       m_tready,
  output  reg [4*DWIDTH-1:0]  m_tdata,
  output  reg [4*KWIDTH-1:0]  m_tkeep,
  output  reg                 m_tlast
  );
  
  reg [1:0]           beat_count = 0;
  reg [3*DWIDTH-1:0]  tdata = 0;
  reg [3*KWIDTH-1:0]  tkeep = 0;
  
  always @(posedge clk)
  begin
    if (rst || (s_tvalid && s_tready && s_tlast))
      beat_count <= 0;
    else if(s_tvalid && s_tready)
      beat_count <= beat_count + 1;
  end
  
  always @(posedge clk)
  begin
    if(s_tvalid && s_tready)
    begin
      case(beat_count)
        2'b00:
        begin
          tdata[DWIDTH-1:0] <= s_tdata;
          tkeep[KWIDTH-1:0] <= s_tkeep;
        end
        2'b01:
        begin
          tdata[2*DWIDTH-1:DWIDTH] <= s_tdata;
          tkeep[2*KWIDTH-1:KWIDTH] <= s_tkeep;
        end
        2'b10:
        begin
          tdata[3*DWIDTH-1:2*DWIDTH] <= s_tdata;
          tkeep[3*KWIDTH-1:2*KWIDTH] <= s_tkeep;
        end
      endcase
    end
  end
  
  always @*
  begin
    s_tready <= ((beat_count == 2'b11) || s_tlast) ? m_tready : 1'b1;
    m_tvalid <= ((beat_count == 2'b11) || s_tlast) && s_tvalid;
    m_tlast <= s_tlast;
    if (s_tlast)
      case (beat_count)
        2'b00:
        begin
          m_tdata <= {{(3*DWIDTH){1'b0}}, s_tdata};
          m_tkeep <= {{(3*KWIDTH){1'b0}}, s_tkeep};
        end
        2'b01:
        begin
          m_tdata <= {{(2*DWIDTH){1'b0}}, s_tdata, tdata[DWIDTH-1:0]};
          m_tkeep <= {{(2*KWIDTH){1'b0}}, s_tkeep, tkeep[KWIDTH-1:0]};
        end
        2'b10:
        begin
          m_tdata <= {{(1*DWIDTH){1'b0}}, s_tdata, tdata[2*DWIDTH-1:0]};
          m_tkeep <= {{(1*KWIDTH){1'b0}}, s_tkeep, tkeep[2*KWIDTH-1:0]};
        end
        2'b11:
        begin
          m_tdata <= {s_tdata, tdata[3*DWIDTH-1:0]};
          m_tkeep <= {s_tkeep, tkeep[3*KWIDTH-1:0]};
        end
      endcase
    else
    begin
      m_tdata <= {s_tdata, tdata};
      m_tkeep <= {s_tkeep, tkeep};
    end
  end
endmodule

module axis_half_width #(
  parameter DWIDTH = 16,
  parameter KWIDTH = DWIDTH/8
)(
  input                   clk,
  input                   rst,
      
  input                   s_tvalid,
  output                  s_tready,
  input   [DWIDTH-1:0]    s_tdata,
  input   [KWIDTH-1:0]    s_tkeep,
  input                   s_tlast,

  output                  m_tvalid,
  input                   m_tready,
  output  [DWIDTH/2-1:0]  m_tdata,
  output  [KWIDTH/2-1:0]  m_tkeep,
  output                  m_tlast
  );
  
  reg [0:0]   beat_count;
  
  always @(posedge clk)
  begin
    if (rst || (m_tvalid && m_tready && m_tlast))
      beat_count <= 0;
    else if(m_tvalid && m_tready)
      beat_count <= beat_count + 1;
  end
  
  wire hi_tkeep = |s_tkeep[KWIDTH-1:KWIDTH/2];
  
  assign s_tready = (beat_count[0] || (!hi_tkeep && s_tlast)) && m_tready;
  assign m_tvalid = s_tvalid;
  assign m_tdata = beat_count[0] ? s_tdata[DWIDTH-1:DWIDTH/2] : s_tdata[DWIDTH/2-1:0];
  assign m_tkeep = beat_count[0] ? s_tkeep[KWIDTH-1:KWIDTH/2] : s_tkeep[KWIDTH/2-1:0];
  assign m_tlast = (hi_tkeep ~^ beat_count[0]) && s_tlast; 
endmodule

module axis_quarter_width #(
  parameter DWIDTH = 32,
  parameter KWIDTH = DWIDTH/8
)(
  input                       clk,
  input                       rst,
      
  input                       s_tvalid,
  output  reg                 s_tready,
  input       [DWIDTH-1:0]    s_tdata,
  input       [KWIDTH-1:0]    s_tkeep,
  input                       s_tlast,

  output  reg                 m_tvalid,
  input                       m_tready,
  output  reg [DWIDTH/4-1:0]  m_tdata,
  output  reg [KWIDTH/4-1:0]  m_tkeep,
  output  reg                 m_tlast
  );
  
  localparam SDWIDTH = DWIDTH/4;
  localparam SKWIDTH = KWIDTH/4;
  
  reg [1:0]   beat_count;
  
  always @(posedge clk)
  begin
    if (rst || (m_tvalid && m_tready && m_tlast))
      beat_count <= 0;
    else if(m_tvalid && m_tready)
      beat_count <= beat_count + 1;
  end

  always @*
  begin
    s_tready <= ((beat_count == 2'b11) || m_tlast) && m_tvalid && m_tready;
    m_tvalid <= s_tvalid;
    case (beat_count)
      2'b00:
      begin
        m_tdata <= s_tdata[1*SDWIDTH-1:0*SDWIDTH];
        m_tkeep <= s_tkeep[1*SKWIDTH-1:0*SKWIDTH];
        m_tlast <= s_tlast && !(|s_tkeep[4*SKWIDTH-1:1*SKWIDTH]);
      end
      2'b01:
      begin
        m_tdata <= s_tdata[2*SDWIDTH-1:1*SDWIDTH];
        m_tkeep <= s_tkeep[2*SKWIDTH-1:1*SKWIDTH];
        m_tlast <= s_tlast && !(|s_tkeep[4*SKWIDTH-1:2*SKWIDTH]);
      end
      2'b10:
      begin
        m_tdata <= s_tdata[3*SDWIDTH-1:2*SDWIDTH];
        m_tkeep <= s_tkeep[3*SKWIDTH-1:2*SKWIDTH];
        m_tlast <= s_tlast && !(|s_tkeep[4*SKWIDTH-1:3*SKWIDTH]);
      end
      2'b11:
      begin
        m_tdata <= s_tdata[4*SDWIDTH-1:3*SDWIDTH];
        m_tkeep <= s_tkeep[4*SKWIDTH-1:3*SKWIDTH];
        m_tlast <= s_tlast;
      end
    endcase
  end
endmodule

module axis_mux #(
  parameter DWIDTH = 8,
  parameter KWIDTH = DWIDTH/8
)(
  input                 clk,
  input                 rst,

  input                 s0_tvalid,
  output                s0_tready,
  input   [DWIDTH-1:0]  s0_tdata,
  input   [KWIDTH-1:0]  s0_tkeep,
  input                 s0_tlast,
      
  input                 s1_tvalid,
  output                s1_tready,
  input   [DWIDTH-1:0]  s1_tdata,
  input   [KWIDTH-1:0]  s1_tkeep,
  input                 s1_tlast,

  output                m_tvalid,
  input                 m_tready,
  output  [DWIDTH-1:0]  m_tdata,
  output  [KWIDTH-1:0]  m_tkeep,
  output                m_tlast
);

  reg [1:0] rChannelGrant, sChannelGrant;
  
  always @(posedge clk)
  begin
    if (rst)
      rChannelGrant <= 0;
    else
      rChannelGrant <= sChannelGrant;
  end
  
  always @*
  begin
    sChannelGrant <= rChannelGrant;
    case (rChannelGrant)
      2'b00:
        if (s0_tvalid)
          sChannelGrant <= 2'b01;
        else if (s1_tvalid)
          sChannelGrant <= 2'b10;
      2'b01:
        if (m_tvalid && m_tready && m_tlast)
        begin
          if (s1_tvalid)
            sChannelGrant <= 2'b10;
          else
            sChannelGrant <= 2'b00;
        end
      2'b10:
        if (m_tvalid && m_tready && m_tlast)
        begin
          if (s0_tvalid)
            sChannelGrant <= 2'b01;
          else
            sChannelGrant <= 2'b00;
        end
      default:
        sChannelGrant <= 2'b00;
    endcase
  end
  
  assign s0_tready = (rChannelGrant == 2'b01) ? m_tready : 1'b0;
  assign s1_tready = (rChannelGrant == 2'b10) ? m_tready : 1'b0;
  assign m_tvalid = (rChannelGrant == 2'b01) ? s0_tvalid :
                    (rChannelGrant == 2'b10) ? s1_tvalid : 1'b0;
  assign m_tdata =  (rChannelGrant == 2'b01) ? s0_tdata :
                    (rChannelGrant == 2'b10) ? s1_tdata : {(DWIDTH){1'b0}};
  assign m_tkeep =  (rChannelGrant == 2'b01) ? s0_tkeep :
                    (rChannelGrant == 2'b10) ? s1_tkeep : {(KWIDTH){1'b0}};
  assign m_tlast =  (rChannelGrant == 2'b01) ? s0_tlast :
                    (rChannelGrant == 2'b10) ? s1_tlast : 1'b0;
endmodule

module axis_async_fifo128 (
  input           rst,
  
  input           s_clk,  
  input           s_tvalid,
  output          s_tready,
  input  [127:0]  s_tdata,
  input  [3:0]    s_tstrb,
  input           s_tlast,
         
  input           m_clk,
  output          m_tvalid,
  input           m_tready,
  output [127:0]  m_tdata,
  output [3:0]    m_tstrb,
  output          m_tlast
  );

  wire [63:0] fifo0_di = s_tdata[63:0];
  wire [63:0] fifo1_di = s_tdata[127:64];
  wire [7:0]  fifo0_dip = {s_tlast, 5'b0, s_tstrb[1:0]};
  wire [7:0]  fifo1_dip = {6'b0, s_tstrb[3:2]};
  wire [63:0] fifo0_do;
  wire [63:0] fifo1_do;
  wire [7:0]  fifo0_dop;
  wire [7:0]  fifo1_dop;
  wire fifo0_full;
  wire fifo0_empty;
  wire fifo0_rden = m_tvalid && m_tready;
  wire fifo1_rden = m_tvalid && m_tready;
  wire fifo0_wren = s_tvalid && s_tready;
  wire fifo1_wren = s_tvalid && s_tready;
  
  assign s_tready = !fifo0_full;
  assign m_tdata = {fifo1_do, fifo0_do};
  assign m_tstrb = {fifo1_dop[1:0], fifo0_dop[1:0]};
  assign m_tlast = fifo0_dop[7];
  assign m_tvalid = !fifo0_empty;
  
  FIFO36E1 #(
    .ALMOST_EMPTY_OFFSET    (13'h0080),     // Sets the almost empty threshold
    .ALMOST_FULL_OFFSET     (13'h0080),     // Sets almost full threshold
    .DATA_WIDTH             (72),           // Sets data width to 4, 9, 18, 36, or 72
    .DO_REG                 (1),            // Enable output register (0 or 1) Must be 1 if EN_SYN = FALSE
    .EN_ECC_READ            ("FALSE"),      // Enable ECC decoder, TRUE or FALSE
    .EN_ECC_WRITE           ("FALSE"),      // Enable ECC encoder, TRUE or FALSE
    .EN_SYN                 ("FALSE"),      // Specifies FIFO as Asynchronous (FALSE) or Synchronous (TRUE)
    .FIFO_MODE              ("FIFO36_72"),  // Sets mode to FIFO36 or FIFO36_72
    .FIRST_WORD_FALL_THROUGH("TRUE"),       // Sets the FIFO FWFT to TRUE or FALSE
    .INIT                   (72'h0),        // Initial values on output port
    .SRVAL                  (72'h0)         // Set/Reset value for output port
  )
  fifo0 (
    // ECC Signals: 1-bit (each) output: Error Correction Circuitry ports
    .DBITERR        (),           // 1-bit output: double bit error status output
    .ECCPARITY      (),           // 8-bit output: generated error correction parity
    .SBITERR        (),           // 1-bit output: single bit error status output
    // Read Data: 64-bit (each) output: Read output data
    .DO             (fifo0_do),   // 64-bit output: data output
    .DOP            (fifo0_dop),  // 8-bit output: parity data output
    // Status: 1-bit (each) output: Flags and other FIFO status outputs
    .ALMOSTEMPTY    (),           // 1-bit output: almost empty output flag
    .ALMOSTFULL     (),           // 1-bit output: almost full output flag
    .EMPTY          (fifo0_empty),// 1-bit output: empty output flag
    .FULL           (fifo0_full),           // 1-bit output: full output flag
    .RDCOUNT        (),           // 13-bit output: read count output
    .RDERR          (),           // 1-bit output: read error output
    .WRCOUNT        (),           // 13-bit output: write count output
    .WRERR          (),           // 1-bit output: write error
    // ECC Signals: 1-bit (each) input: Error Correction Circuitry ports
    .INJECTDBITERR  (1'b0),       // 1-bit input: Inject a double bit error
    .INJECTSBITERR  (1'b0),
    // Read Control Signals: 1-bit (each) input: Read clock, enable and reset input signals
    .RDCLK          (m_clk),      // 1-bit input: read clock input
    .RDEN           (fifo0_rden), // 1-bit input: read enable input
    .REGCE          (1'b1),       // 1-bit input: clock enable input
    .RST            (rst),        // 1-bit input: reset input
//        .RSTREG         (),     // 1-bit input: output register set/reset //RSTREG can only be used when EN_SYN is TRUE and DO_REG is 1 for FIFO operation
    // Write Control Signals: 1-bit (each) input: Write clock and enable input signals
    .WRCLK          (s_clk),      // 1-bit input: write clock input
    .WREN           (fifo0_wren), // 1-bit input: write enable input
    // Write Data: 64-bit (each) input: Write input data
    .DI             (fifo0_di),   // 64-bit input: data input
    .DIP            (fifo0_dip)   // 8-bit input: parity input
  );

  FIFO36E1 #(
    .ALMOST_EMPTY_OFFSET    (13'h0080),     // Sets the almost empty threshold
    .ALMOST_FULL_OFFSET     (13'h0080),     // Sets almost full threshold
    .DATA_WIDTH             (72),           // Sets data width to 4, 9, 18, 36, or 72
    .DO_REG                 (1),            // Enable output register (0 or 1) Must be 1 if EN_SYN = FALSE
    .EN_ECC_READ            ("FALSE"),      // Enable ECC decoder, TRUE or FALSE
    .EN_ECC_WRITE           ("FALSE"),      // Enable ECC encoder, TRUE or FALSE
    .EN_SYN                 ("FALSE"),      // Specifies FIFO as Asynchronous (FALSE) or Synchronous (TRUE)
    .FIFO_MODE              ("FIFO36_72"),  // Sets mode to FIFO36 or FIFO36_72
    .FIRST_WORD_FALL_THROUGH("TRUE"),       // Sets the FIFO FWFT to TRUE or FALSE
    .INIT                   (72'h0),        // Initial values on output port
    .SRVAL                  (72'h0)         // Set/Reset value for output port
  )
  fifo1 (
    // ECC Signals: 1-bit (each) output: Error Correction Circuitry ports
    .DBITERR        (),           // 1-bit output: double bit error status output
    .ECCPARITY      (),           // 8-bit output: generated error correction parity
    .SBITERR        (),           // 1-bit output: single bit error status output
    // Read Data: 64-bit (each) output: Read output data
    .DO             (fifo1_do),   // 64-bit output: data output
    .DOP            (fifo1_dop),  // 8-bit output: parity data output
    // Status: 1-bit (each) output: Flags and other FIFO status outputs
    .ALMOSTEMPTY    (),           // 1-bit output: almost empty output flag
    .ALMOSTFULL     (),           // 1-bit output: almost full output flag
    .EMPTY          (),           // 1-bit output: empty output flag
    .FULL           (),           // 1-bit output: full output flag
    .RDCOUNT        (),           // 13-bit output: read count output
    .RDERR          (),           // 1-bit output: read error output
    .WRCOUNT        (),           // 13-bit output: write count output
    .WRERR          (),           // 1-bit output: write error
    // ECC Signals: 1-bit (each) input: Error Correction Circuitry ports
    .INJECTDBITERR  (1'b0),       // 1-bit input: Inject a double bit error
    .INJECTSBITERR  (1'b0),
    // Read Control Signals: 1-bit (each) input: Read clock, enable and reset input signals
    .RDCLK          (m_clk),      // 1-bit input: read clock input
    .RDEN           (fifo1_rden), // 1-bit input: read enable input
    .REGCE          (1'b1),       // 1-bit input: clock enable input
    .RST            (rst),        // 1-bit input: reset input
//        .RSTREG         (),     // 1-bit input: output register set/reset //RSTREG can only be used when EN_SYN is TRUE and DO_REG is 1 for FIFO operation
    // Write Control Signals: 1-bit (each) input: Write clock and enable input signals
    .WRCLK          (s_clk),      // 1-bit input: write clock input
    .WREN           (fifo1_wren), // 1-bit input: write enable input
    // Write Data: 64-bit (each) input: Write input data
    .DI             (fifo1_di),   // 64-bit input: data input
    .DIP            (fifo1_dip)   // 8-bit input: parity input
  );
  
endmodule
